%%%====================================================================
%%% File    : pipe_join_lookup.erl
%%% Author  :  <vjache>
%%% Description: This is a simpliest pipe implementation. It may be 
%%%				 considered as a simple reference implementation of 
%%%				 a pipe callback module, and could be used for debug 
%%%				 purposes.
%%%
%%% Created : 24.03.2009
%%%====================================================================

-module(pipe_join_lookup).

%%
%% Include files
%%
-include("../include/pipe.hrl").
-include("../include/util.hrl").
%%
%% Exported Functions
%%
-export([
		 new/2,
		 new/3,
		 new/4,
		 open/1,
		 push/2,
		 flush/1,
		 close/1,
		 test/1,
		 test/2]).

%%====================================================================
%% API Functions
%%====================================================================

%%--------------------------------------------------------------------
%% Function: 	new(LookupFunList,NextPipe) -> Pipe
%% Description: Creates a pipe specification using list of lookup 
%%				functions (LookupFunList) and destination pipe (NextPipe).
%%--------------------------------------------------------------------
new(LookupFunList,NextPipe) ->
	pipe:pipe_from_module(?MODULE, {LookupFunList,10,tup,[]}, NextPipe).

%%--------------------------------------------------------------------
%% Function: 	new(LookupFunList,Kind,NextPipe) -> Pipe
%% Description: Creates a pipe specification using list of lookup 
%%				functions (LookupFunList), kind of flattening (Kind) 
%%				and destination pipe (NextPipe). Kind of flattening
%%				controls the output format, if 'ls' then each row will 
%%				be represented as a list of results of lookup func, if 
%%				'tup' then each row will be represented as a tuple of 
%%				results of lookup func.
%%--------------------------------------------------------------------
new(LookupFunList,Kind,NextPipe) ->
	pipe:pipe_from_module(?MODULE, {LookupFunList,Kind,[]}, NextPipe).

%%--------------------------------------------------------------------
%% Function: 	new(LookupFunList,Kind,Tag,NextPipe) -> Pipe
%% Description: Creates a pipe specification using list of lookup 
%%				functions (LookupFunList), kind of flattening (Kind),
%%				tag (Tag) and destination pipe (NextPipe). Kind of flattening
%%				controls the output format, if 'ls' then each row will 
%%				be represented as a list of results of lookup func, if 
%%				'tup' then each row will be represented as a tuple of 
%%				results of lookup func. Tag is an any term that will be 
%%				placed to the first position of list or tuple depending 
%%				on kind of flattening.
%%--------------------------------------------------------------------
new(LookupFunList,Kind,Tag,NextPipe) ->
	pipe:pipe_from_module(?MODULE, {LookupFunList,Kind,[Tag]}, NextPipe).

open({{LookupFunList,Kind,TagL},NextPipe})-> %%PipeState
	{LookupFunList,Kind,TagL,_NextPipeState=pipe:open(NextPipe)}.
push({LookupFunList,Kind,TagL,NextPipeState},?REJECT_REC) ->
	throw({reject_record_disallowed});
push({LookupFunList,Kind,TagL,NextPipeState},Item) ->
	IterJL=case Kind of
		ls ->
			case TagL of
				[] ->
					iter_join_lookup:new_flat_ls(
					  iter:single(Item), LookupFunList);
				[Tag] ->
					iter_join_lookup:new_flat_ls(
					  iter:single(Item), LookupFunList,Tag)
			end;
		tup ->
			case TagL of
				[] ->
%% 					throw(user),
%% 					io:format("push ~p~n",[Item]),
					iter_join_lookup:new_flat_tup(
					  iter:single(Item), LookupFunList);
				[Tag] ->
					iter_join_lookup:new_flat_tup(
					  iter:single(Item), LookupFunList,Tag)
			end
	end,
	{IterStateFin,NextPipeStateNew}=pipe:push_each(NextPipeState, IterJL),
	ok=iter:close(IterStateFin),
	{LookupFunList,Kind,TagL,NextPipeStateNew}.
flush(S) ->
	S.
close({_LookupFunList,_Kind,_TagL,NextPipeState}) ->
	ok=pipe:close(NextPipeState).
	

%%====================================================================
%% Test Functions
%%====================================================================

test(KeyList) ->
	D1=dict:from_list([
	 {k1,val1},
	 {k2,val2},
	 {k3,val3}
	 ]),
	D2=dict:from_list([
	 {k1,val21},
	 {k2,val22},
	 {k3,val23},
	 {k4,val24}
	 ]),
	S=pipe:open(new([
					 fun(K)->
						dict_lookup(K,D1)
					 end,
					 fun(K)->
						dict_lookup(K,D2)
					 end,
					 fun(K)->
						dict_lookup(K,D2)
					 end
					 ],pipe_console:new())),
	{IterState,PipeState}=pipe:push_each(S, iter:from_list(KeyList)),
	iter:close(IterState),
	pipe:close(PipeState).
test(ets,KeyList) ->
	Tid1=ets:new(a, [bag]),
	Tid2=ets:new(b, [bag]),
	Tid3=ets:new(c, [bag]),
	ets:insert(Tid1,[
	 {k1,val1},
	 {k2,val2},
	 {k3,val3},
	 {k6,val6}
	 ]),
	ets:insert(Tid2,[
	 {k1,val21},
	 {k2,val22},
	 {k3,val23},
	 {k4,val24},
	 {k6,val26}
	 ]),
	ets:insert(Tid3,[
	 {k0,val30},
	 {k2,val32},
	 {k3,val33},
	 {k4,val34},
	 {k5,val35},
	 {k6,val361},
	 {k6,val362},
	 {k6,val363},
	 {k6,val364},
	 {k6,val365},
	 {k6,val366},
	 {k6,val367},
	 {k6,val368}
	 ]),
	S=pipe:open(new([
					 fun(K)->
						ets_lookup(K,Tid1)
					 end,
					 fun(K)->
						ets_lookup(K,Tid2)
					 end,
					 fun(K)->
						ets_lookup(K,Tid3)
					 end
					 ],ls,pipe_console:new())),
	{IterState,PipeState}=pipe:push_each(S, iter:from_list(KeyList)),
	iter:close(IterState),
	pipe:close(PipeState).

dict_lookup(K,Dict)->
	L=case dict:find(K, Dict) of
		  error ->
			  [];
		  {ok,V} ->
			  [V]
	  end,
	iter:from_list(L).
ets_lookup(K,Tid)->
	iter_on_empty:new(iter:from_list(ets:lookup(Tid, K)),iter:single('$NULL')).
%%====================================================================
%% Local Functions
%%====================================================================

